#include <sys/select.h>
#include <unistd.h>
#include <cerrno>
#include <ctime>
#include <cstring>
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define DEL -1

using namespace std;

int fdsArray[sizeof(fd_set) * 8];
int gnum = sizeof(fdsArray) / sizeof(int);

class TcpServer
{
public:
    static const int gbacklog = 20;

    static int Socket()
    {
        int listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (listenSock < 1)
        {
            exit(1);
        }
        int opt = 1;
        setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        return listenSock;
    }
    static void Bind(int socket, uint16_t port)
    {
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;
        if (bind(socket, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            exit(2);
        }
    }

    static void Listen(int socket)
    {
        if (listen(socket, gbacklog) < 0)
        {
            exit(3);
        }
    }
    static int Accept(int socket, std::string &ip, uint16_t &port)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);

        int clientSock = accept(socket, (struct sockaddr *)&client, &len);
        if (clientSock < 0)
        {
            return -1;
        }

        ip = inet_ntoa(client.sin_addr);
        port = ntohs(client.sin_port);
        return clientSock;
    }
};

static void showArray(int arr[], int num)
{
    cout << "当前合法sock list# ";
    for (int i = 0; i < num; ++i)
    {
        if (arr[i] == DEL)
        {
            continue;
        }
        else
            cout << arr[i] << " ";
    }
    cout << endl;
}

void HandleEvent(int listenSock, fd_set &readers)
{
    for (int i = 0; i < gnum; ++i)
    {
        if (fdsArray[i] == DEL)
        {
            continue;
        }
        if (fdsArray[i] == listenSock)
        {
            if (FD_ISSET(listenSock, &readers))
            {
                cout << "已经有一个新的链接到来了，需要进行读取了" << endl;
                string ip;
                uint16_t port;
                int sock = TcpServer::Accept(listenSock, ip, port);
                if (sock < 0)
                {
                    return;
                }
                cout << "获取新连接成功: " << ip << ":" << port << endl;
                int i = 0;
                for (; i < gnum; ++i)
                {
                    if (fdsArray[i] == DEL)
                    {
                        break;
                    }
                }
                if (i == gnum)
                {
                    cerr << "服务器已经满了" << endl;
                    close(sock);
                }
                else
                {
                    cout << sock << "添加到" << i << "号位置" << endl;
                    fdsArray[i] = sock;
                    showArray(fdsArray, gnum);
                }
            }
        }
        else
        {
            if (FD_ISSET(fdsArray[i], &readers))
            {
                char buffer[1024];
                ssize_t s = recv(fdsArray[i], buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = '\0';
                    printf("[%d]: %s\n", fdsArray[i], buffer);
                }
                else if (s == 0)
                {
                    printf("[%d]退出\n", fdsArray[i]);
                    close(fdsArray[i]);
                    fdsArray[i] = DEL;
                    showArray(fdsArray, gnum);
                }
                else
                {
                    printf("[%d]退出\n", fdsArray[i]);
                    close(fdsArray[i]);
                    fdsArray[i] = DEL;
                    showArray(fdsArray, gnum);
                }
            }
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        return 1;
    }
    int listenSock = TcpServer::Socket();
    TcpServer::Bind(listenSock, atoi(argv[1]));
    TcpServer::Listen(listenSock);
    for (int i = 1; i < gnum; ++i)
    {
        fdsArray[i] = DEL;
    }
    fdsArray[0] = listenSock;
    while (true)
    {
        int maxFd = DEL;
        fd_set readers;
        FD_ZERO(&readers);
        for (int i = 0; i < gnum; ++i)
        {
            if (fdsArray[i] == DEL)
            {
                continue;
            }
            FD_SET(fdsArray[i], &readers);
            if (fdsArray[i] > maxFd)
            {
                maxFd = fdsArray[i];
            }
        }
        struct timeval timeout = {5, 0};
        int n = select(maxFd + 1, &readers, nullptr, nullptr, &timeout);
        switch (n)
        {
        case 0:
            cout << "time out..." << (unsigned int)time(nullptr) << endl;
            break;
        case -1:
            cerr << errno << ":" << strerror(errno) << endl;
            break;
        default:
            HandleEvent(listenSock, readers);
        }
    }
    return 0;
}
